/*
  http://github.com/danpalmer/jquery.complexify.js

  This code is distributed under the WTFPL v2:
*/
(function ($) {

  $.fn.extend({
    complexify: function(options, callback) {

      var MIN_COMPLEXITY = 49; // 12 chars with Upper, Lower and Number
      var MAX_COMPLEXITY = 120; //  25 chars, all charsets
      var CHARSETS = [
        // Commonly Used
        ////////////////////
        [0x0020, 0x0020], // Space
        [0x0030, 0x0039], // Numbers
        [0x0041, 0x005A], // Uppercase
        [0x0061, 0x007A], // Lowercase
        [0x0021, 0x002F], // Punctuation
        [0x003A, 0x0040], // Punctuation
        [0x005B, 0x0060], // Punctuation
        [0x007B, 0x007E], // Punctuation
        // Everything Else
        ////////////////////
        [0x0080, 0x00FF], // Latin-1 Supplement
        [0x0100, 0x017F], // Latin Extended-A
        [0x0180, 0x024F], // Latin Extended-B
        [0x0250, 0x02AF], // IPA Extensions
        [0x02B0, 0x02FF], // Spacing Modifier Letters
        [0x0300, 0x036F], // Combining Diacritical Marks
        [0x0370, 0x03FF], // Greek
        [0x0400, 0x04FF], // Cyrillic
        [0x0530, 0x058F], // Armenian
        [0x0590, 0x05FF], // Hebrew
        [0x0600, 0x06FF], // Arabic
        [0x0700, 0x074F], // Syriac
        [0x0780, 0x07BF], // Thaana
        [0x0900, 0x097F], // Devanagari
        [0x0980, 0x09FF], // Bengali
        [0x0A00, 0x0A7F], // Gurmukhi
        [0x0A80, 0x0AFF], // Gujarati
        [0x0B00, 0x0B7F], // Oriya
        [0x0B80, 0x0BFF], // Tamil
        [0x0C00, 0x0C7F], // Telugu
        [0x0C80, 0x0CFF], // Kannada
        [0x0D00, 0x0D7F], // Malayalam
        [0x0D80, 0x0DFF], // Sinhala
        [0x0E00, 0x0E7F], // Thai
        [0x0E80, 0x0EFF], // Lao
        [0x0F00, 0x0FFF], // Tibetan
        [0x1000, 0x109F], // Myanmar
        [0x10A0, 0x10FF], // Georgian
        [0x1100, 0x11FF], // Hangul Jamo
        [0x1200, 0x137F], // Ethiopic
        [0x13A0, 0x13FF], // Cherokee
        [0x1400, 0x167F], // Unified Canadian Aboriginal Syllabics
        [0x1680, 0x169F], // Ogham
        [0x16A0, 0x16FF], // Runic
        [0x1780, 0x17FF], // Khmer
        [0x1800, 0x18AF], // Mongolian
        [0x1E00, 0x1EFF], // Latin Extended Additional
        [0x1F00, 0x1FFF], // Greek Extended
        [0x2000, 0x206F], // General Punctuation
        [0x2070, 0x209F], // Superscripts and Subscripts
        [0x20A0, 0x20CF], // Currency Symbols
        [0x20D0, 0x20FF], // Combining Marks for Symbols
        [0x2100, 0x214F], // Letterlike Symbols
        [0x2150, 0x218F], // Number Forms
        [0x2190, 0x21FF], // Arrows
        [0x2200, 0x22FF], // Mathematical Operators
        [0x2300, 0x23FF], // Miscellaneous Technical
        [0x2400, 0x243F], // Control Pictures
        [0x2440, 0x245F], // Optical Character Recognition
        [0x2460, 0x24FF], // Enclosed Alphanumerics
        [0x2500, 0x257F], // Box Drawing
        [0x2580, 0x259F], // Block Elements
        [0x25A0, 0x25FF], // Geometric Shapes
        [0x2600, 0x26FF], // Miscellaneous Symbols
        [0x2700, 0x27BF], // Dingbats
        [0x2800, 0x28FF], // Braille Patterns
        [0x2E80, 0x2EFF], // CJK Radicals Supplement
        [0x2F00, 0x2FDF], // Kangxi Radicals
        [0x2FF0, 0x2FFF], // Ideographic Description Characters
        [0x3000, 0x303F], // CJK Symbols and Punctuation
        [0x3040, 0x309F], // Hiragana
        [0x30A0, 0x30FF], // Katakana
        [0x3100, 0x312F], // Bopomofo
        [0x3130, 0x318F], // Hangul Compatibility Jamo
        [0x3190, 0x319F], // Kanbun
        [0x31A0, 0x31BF], // Bopomofo Extended
        [0x3200, 0x32FF], // Enclosed CJK Letters and Months
        [0x3300, 0x33FF], // CJK Compatibility
        [0x3400, 0x4DB5], // CJK Unified Ideographs Extension A
        [0x4E00, 0x9FFF], // CJK Unified Ideographs
        [0xA000, 0xA48F], // Yi Syllables
        [0xA490, 0xA4CF], // Yi Radicals
        [0xAC00, 0xD7A3], // Hangul Syllables
        [0xD800, 0xDB7F], // High Surrogates
        [0xDB80, 0xDBFF], // High Private Use Surrogates
        [0xDC00, 0xDFFF], // Low Surrogates
        [0xE000, 0xF8FF], // Private Use
        [0xF900, 0xFAFF], // CJK Compatibility Ideographs
        [0xFB00, 0xFB4F], // Alphabetic Presentation Forms
        [0xFB50, 0xFDFF], // Arabic Presentation Forms-A
        [0xFE20, 0xFE2F], // Combining Half Marks
        [0xFE30, 0xFE4F], // CJK Compatibility Forms
        [0xFE50, 0xFE6F], // Small Form Variants
        [0xFE70, 0xFEFE], // Arabic Presentation Forms-B
        [0xFEFF, 0xFEFF], // Specials
        [0xFF00, 0xFFEF], // Halfwidth and Fullwidth Forms
        [0xFFF0, 0xFFFD]  // Specials
      ];

      var defaults = {
        minimumChars: 8,
        strengthScaleFactor: 1,
        bannedPasswords: window.COMPLEXIFY_BANLIST || [],
        banMode: 'strict' // (strict|loose)
      };

      if($.isFunction(options) && !callback) {
        callback = options;
        options = {};
      }

      options = $.extend(defaults, options);

      function additionalComplexityForCharset(str, charset, t) {
        var sum = 0;
        for (var i = str.length - 1; i >= 0; i--) {
          if (charset[0] <= str.charCodeAt(i) && str.charCodeAt(i) <= charset[1]) {
            // 0 4 6 8 12
            if (t== 0) {
              sum += 0;
            } else if (t == 1) {
              sum += 4;
            } else if (t == 2) {
              sum += 8;
            } else if (t == 3) {
              sum += 6;
            } else {
              sum += 12;
            }
            // return charset[1] - charset[0] + 1;
          }
        }
        return sum;
      }

      function inBanlist(str) {
        if (options.banMode === 'strict') {
          for (var i = 0; i < options.bannedPasswords.length; i++) {
            if (str.toLowerCase().indexOf(options.bannedPasswords[i].toLowerCase()) !== -1) {
                return true;
            }
          }
          return false;
        } else {
          return $.inArray(str, options.bannedPasswords) > -1 ? true : false;
        }
      }

      function evaluateSecurity() {
        var password = $(this).val();
        var complexity = 0, valid = false;

        // Reset complexity to 0 when banned password is found
        if (!inBanlist(password)) {

          // Add character complexity
          for (var i = CHARSETS.length - 1; i >= 0; i--) {
            complexity += additionalComplexityForCharset(password, CHARSETS[i], i);
          }

        } else {
          complexity = 1;
        }

        // Use natural log to produce linear scale
        // complexity = Math.log(Math.pow(complexity, password.length)) * (1/options.strengthScaleFactor);

        valid = (complexity > MIN_COMPLEXITY && password.length >= options.minimumChars);

        // Scale to percentage, so it can be used for a progress bar
        complexity = (complexity / MAX_COMPLEXITY) * 100;
        complexity = (complexity > 100) ? 100 : complexity;

        callback.call(this, valid, complexity);
      }

      this.each(function () {
      	if($(this).val()) {
          evaluateSecurity.apply(this);
        }
      });

      return this.each(function () {
        $(this).bind('keyup focus input propertychange mouseup', evaluateSecurity);
      });

    }
  });

})(jQuery);
